自己第一次聽到 Curry 腦中只浮現以上 XD,認真研究後也是有看沒懂。但比較習慣用 Functional Programming 寫程式後就發現其實 Currying 概念不斷貫穿在其中,希望這篇能用淺顯的敘述來介紹它。
喔對了為什麼會叫 Currying 是因為是因為這個方法的概念的建立者叫做 Haskell Curry。
Transforms a function with multiple arguments to a chain of function applications with one argument each
簡單來說就是 “把接受多個參數的函數變換成接受一個單一參數就叫做 Currying” 。直接來看例子比較清楚,以下是一個單純做變數相加的函式
const add = (a, b) => a + b;
add(1, 2) // 3
改成 Currying 後就會變
const add = a => b => a + b;
add(1)(2) // 3
// ---- below is common way -------
const add1 = add(1)
add1(2) // 3
你會發現 Currying 一次只接受 “一個” 參數
f = (a, b) => value
f(a, b)
// become currying
f = a => b => value
g = f(a); // return function b => value
g(b); // return value;
// 也可以
f(a)(b)
export default function curry(func) {
return function inner(...args) {
if (args.length === func.length) {
return func.apply(this, args);
} else {
return (arg) => {
return arg === undefined
? inner.apply(this, [...args])
: inner.apply(this, [...args, arg]);
};
}
};
}
它的原理也很簡單,利用 closure 特性,將 f(a) 存放在 g 中 ,待最後參數 b 傳入,完成運算 g(b)。
以下是一個判斷你分數有沒有有 Pass 的簡單函式
const uncurriedGradeTest=(passGrade, failGrade, average, testScore) =>
testScore >= average ? passGrade : failGrade;
// Repeat the same args many times
uncurriedGradeTest( 'Pass', 'Fail', 0.2, 0.19 )
uncurriedGradeTest( 'Pass', 'Fail', 0.2, 0.39 )
uncurriedGradeTest( 'Pass', 'Fail', 0.2, 0.5 )
uncurriedGradeTest( 'Pass', 'Fail', 0.2, 0.1 )
uncurriedGradeTest( 'Pass', 'Fail', 0.2, 0.8 )
你會發現一直需要丟重覆的參數 Pass 、Fail 跟平均分數 0.2,那換成 Currying 呢?
// Currying
const getGradeTest = (passGrade, failGrade) => average => testScore =>
testScore >= average ? passGrade : failGrade;
const passFaillTest = getGradeTest( 'Pass', 'Fail' )( 0.2 );
passFaillTest( 0.19 )
passFaillTest( 0.39 )
passFaillTest( 0.5 )
passFaillTest( 0.1 )
passFaillTest( 0.8 )
是不是乾淨多了呢!
常看到有人把這兩個當成同樣東西,事實是他們的確很像,也都是利用 closure 特性但卻是不一樣的
// Currying
const add = a => b => c => a + b + c;
add(1)(2)(3) // 6
// Partial application
const add = a => (b, c) => a + b + c;
add(1)(2, 3) // 6
-|currying | Partial application
------------- | -------------
arity | 1 | variable (f, ...args)
bind arguments? | No | Yes
所以最大不同就是 currying 只接收一個 Arity,而 Partial application 可以接受多個
the number of arguments of a function
f( a ) // arity: 1
f( a, b ) // arity: 2
f(...args) // arity: 0
// partial Application
const partial =
(f, ...args1) =>
(...args2) =>
f(...args1 , ...args2);
const volume = (a, b c) => a*b*c;
console.log( partial( volume, 2, 3 )( 4 ) ) // 24
console.log( partial( volume, 2 )( 3, 4 ) ) // 24
f.bind(null, ...args)
下一篇會舉大量例子讓大家更了解 Curry
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。